package com.insight.service;

import com.insight.common.enums.DatabaseType;
import com.insight.dto.datasource.DataSourceCreateRequest;
import com.insight.dto.datasource.DataSourceResponse;
import com.insight.dto.datasource.DataSourceUpdateRequest;
import com.insight.entity.DataSource;
import com.insight.common.exception.DuplicateResourceException;
import com.insight.common.exception.ResourceNotFoundException;
import com.insight.mapper.DataSourceMapper;
import com.insight.repository.DataSourceRepository;
import com.insight.common.util.EncryptionUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * T035: 数据源服务层
 * 实现数据源的CRUD业务逻辑，包括密码加密/解密
 *
 * @author Insight Team
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DataSourceService {

    private final DataSourceRepository dataSourceRepository;
    private final DataSourceMapper dataSourceMapper;

    /**
     * 创建数据源
     * 检查名称唯一性，加密密码后保存
     *
     * @param request        创建请求
     * @param encryptionKey  加密密钥
     * @return 数据源响应DTO
     * @throws DuplicateResourceException 如果名称已存在
     */
    @Transactional
    public DataSourceResponse createDataSource(DataSourceCreateRequest request, String encryptionKey) {
        log.info("创建数据源: name={}, dbType={}", request.getName(), request.getDbType());

        // 检查名称唯一性
        if (dataSourceRepository.existsByName(request.getName())) {
            log.warn("数据源名称已存在: {}", request.getName());
            throw new DuplicateResourceException("数据源", "name", request.getName());
        }

        // DTO → Entity
        DataSource dataSource = dataSourceMapper.toEntity(request);

        // 加密密码
        String encryptedPassword = EncryptionUtil.encrypt(request.getPassword(), encryptionKey);
        dataSource.setPasswordEncrypted(encryptedPassword);

        // 保存到数据库
        DataSource savedDataSource = dataSourceRepository.save(dataSource);
        log.info("数据源创建成功: id={}, name={}", savedDataSource.getId(), savedDataSource.getName());

        // Entity → Response DTO
        return dataSourceMapper.toResponse(savedDataSource);
    }

    /**
     * 根据ID查询数据源实体(返回Optional)
     * 用于控制器层需要自定义异常处理的场景
     *
     * @param id 数据源ID
     * @return 数据源Optional
     */
    @Transactional(readOnly = true)
    public java.util.Optional<DataSource> findById(Long id) {
        log.debug("查询数据源实体: id={}", id);
        return dataSourceRepository.findById(id);
    }

    /**
     * 根据ID获取数据源
     *
     * @param id 数据源ID
     * @return 数据源响应DTO
     * @throws ResourceNotFoundException 如果数据源不存在
     */
    @Transactional(readOnly = true)
    public DataSourceResponse getDataSourceById(Long id) {
        log.debug("查询数据源: id={}", id);

        DataSource dataSource = dataSourceRepository.findById(id)
                .orElseThrow(() -> {
                    log.warn("数据源不存在: id={}", id);
                    return new ResourceNotFoundException("数据源", id);
                });

        return dataSourceMapper.toResponse(dataSource);
    }

    /**
     * 根据名称获取数据源
     *
     * @param name 数据源名称
     * @return 数据源响应DTO
     * @throws ResourceNotFoundException 如果数据源不存在
     */
    @Transactional(readOnly = true)
    public DataSourceResponse getDataSourceByName(String name) {
        log.debug("查询数据源: name={}", name);

        DataSource dataSource = dataSourceRepository.findByName(name)
                .orElseThrow(() -> {
                    log.warn("数据源不存在: name={}", name);
                    return new ResourceNotFoundException("数据源", name);
                });

        return dataSourceMapper.toResponse(dataSource);
    }

    /**
     * 分页查询所有数据源
     *
     * @param pageable 分页参数
     * @return 数据源分页结果
     */
    @Transactional(readOnly = true)
    public Page<DataSourceResponse> listDataSources(Pageable pageable) {
        log.debug("分页查询数据源: page={}, size={}", pageable.getPageNumber(), pageable.getPageSize());

        Page<DataSource> dataSourcePage = dataSourceRepository.findAll(pageable);
        return dataSourcePage.map(dataSourceMapper::toResponse);
    }

    /**
     * 根据数据库类型查询数据源
     *
     * @param dbType 数据库类型
     * @return 数据源列表
     */
    @Transactional(readOnly = true)
    public List<DataSourceResponse> listDataSourcesByType(DatabaseType dbType) {
        log.debug("按类型查询数据源: dbType={}", dbType);

        List<DataSource> dataSources = dataSourceRepository.findByDbType(dbType);
        return dataSourceMapper.toResponseList(dataSources);
    }

    /**
     * 查询所有已启用的数据源
     *
     * @return 已启用的数据源列表
     */
    @Transactional(readOnly = true)
    public List<DataSourceResponse> listEnabledDataSources() {
        log.debug("查询所有已启用的数据源");

        List<DataSource> dataSources = dataSourceRepository.findByEnabledTrue();
        return dataSourceMapper.toResponseList(dataSources);
    }

    /**
     * 更新数据源（不包括密码）
     *
     * @param id      数据源ID
     * @param request 更新请求
     * @return 更新后的数据源响应DTO
     * @throws ResourceNotFoundException 如果数据源不存在
     */
    @Transactional
    public DataSourceResponse updateDataSource(Long id, DataSourceUpdateRequest request) {
        log.info("更新数据源: id={}", id);

        // 查找现有数据源
        DataSource dataSource = dataSourceRepository.findById(id)
                .orElseThrow(() -> {
                    log.warn("数据源不存在: id={}", id);
                    return new ResourceNotFoundException("数据源", id);
                });

        // 使用MapStruct更新非null字段
        dataSourceMapper.updateEntityFromRequest(request, dataSource);

        // 保存更新
        DataSource updatedDataSource = dataSourceRepository.save(dataSource);
        log.info("数据源更新成功: id={}, name={}", updatedDataSource.getId(), updatedDataSource.getName());

        return dataSourceMapper.toResponse(updatedDataSource);
    }

    /**
     * 单独更新数据源密码
     *
     * @param id             数据源ID
     * @param newPassword    新密码（明文）
     * @param encryptionKey  加密密钥
     * @throws ResourceNotFoundException 如果数据源不存在
     */
    @Transactional
    public void updatePassword(Long id, String newPassword, String encryptionKey) {
        log.info("更新数据源密码: id={}", id);

        // 查找现有数据源
        DataSource dataSource = dataSourceRepository.findById(id)
                .orElseThrow(() -> {
                    log.warn("数据源不存在: id={}", id);
                    return new ResourceNotFoundException("数据源", id);
                });

        // 加密新密码
        String encryptedPassword = EncryptionUtil.encrypt(newPassword, encryptionKey);
        dataSource.setPasswordEncrypted(encryptedPassword);

        // 保存更新
        dataSourceRepository.save(dataSource);
        log.info("数据源密码更新成功: id={}", id);
    }

    /**
     * 删除数据源
     *
     * @param id 数据源ID
     * @throws ResourceNotFoundException 如果数据源不存在
     */
    @Transactional
    public void deleteDataSource(Long id) {
        log.info("删除数据源: id={}", id);

        // 检查数据源是否存在
        if (!dataSourceRepository.existsById(id)) {
            log.warn("数据源不存在: id={}", id);
            throw new ResourceNotFoundException("数据源", id);
        }

        // 删除数据源
        dataSourceRepository.deleteById(id);
        log.info("数据源删除成功: id={}", id);
    }

    /**
     * 获取解密后的密码（用于连接测试）
     *
     * @param id             数据源ID
     * @param encryptionKey  解密密钥
     * @return 明文密码
     * @throws ResourceNotFoundException 如果数据源不存在
     */
    @Transactional(readOnly = true)
    public String getDecryptedPassword(Long id, String encryptionKey) {
        log.debug("获取数据源密码: id={}", id);

        DataSource dataSource = dataSourceRepository.findById(id)
                .orElseThrow(() -> {
                    log.warn("数据源不存在: id={}", id);
                    return new ResourceNotFoundException("数据源", id);
                });

        // 解密密码
        return EncryptionUtil.decrypt(dataSource.getPasswordEncrypted(), encryptionKey);
    }

    /**
     * 切换数据源启用状态
     *
     * @param id 数据源ID
     * @throws ResourceNotFoundException 如果数据源不存在
     */
    @Transactional
    public void toggleEnabled(Long id) {
        log.info("切换数据源启用状态: id={}", id);

        DataSource dataSource = dataSourceRepository.findById(id)
                .orElseThrow(() -> {
                    log.warn("数据源不存在: id={}", id);
                    return new ResourceNotFoundException("数据源", id);
                });

        // 切换状态
        dataSource.setEnabled(!dataSource.getEnabled());
        dataSourceRepository.save(dataSource);

        log.info("数据源状态已切换: id={}, enabled={}", id, dataSource.getEnabled());
    }

    /**
     * 统计数据源总数
     *
     * @return 数据源总数
     */
    @Transactional(readOnly = true)
    public long countDataSources() {
        return dataSourceRepository.count();
    }

    /**
     * 检查数据源名称是否已存在
     *
     * @param name 数据源名称
     * @return true表示已存在，false表示不存在
     */
    @Transactional(readOnly = true)
    public boolean existsByName(String name) {
        return dataSourceRepository.existsByName(name);
    }

    /**
     * 启用数据源
     */
    @Transactional
    public void enableDataSource(Long id) {
        log.info("启用数据源: id={}", id);
        DataSource dataSource = dataSourceRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("数据源", id));
        dataSource.setEnabled(true);
        dataSourceRepository.save(dataSource);
    }

    /**
     * 禁用数据源
     */
    @Transactional
    public void disableDataSource(Long id) {
        log.info("禁用数据源: id={}", id);
        DataSource dataSource = dataSourceRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("数据源", id));
        dataSource.setEnabled(false);
        dataSourceRepository.save(dataSource);
    }

    /**
     * 批量启用数据源
     */
    @Transactional
    public void batchEnableDataSources(List<Long> ids) {
        log.info("批量启用数据源: ids={}", ids);
        List<DataSource> dataSources = dataSourceRepository.findAllById(ids);
        dataSources.forEach(ds -> ds.setEnabled(true));
        dataSourceRepository.saveAll(dataSources);
    }

    /**
     * 批量禁用数据源
     */
    @Transactional
    public void batchDisableDataSources(List<Long> ids) {
        log.info("批量禁用数据源: ids={}", ids);
        List<DataSource> dataSources = dataSourceRepository.findAllById(ids);
        dataSources.forEach(ds -> ds.setEnabled(false));
        dataSourceRepository.saveAll(dataSources);
    }

    /**
     * 批量删除数据源
     */
    @Transactional
    public void batchDeleteDataSources(List<Long> ids) {
        log.info("批量删除数据源: ids={}", ids);
        dataSourceRepository.deleteAllById(ids);
    }
}
